/* Copyright 2023 Dual Tachyon
 * https://github.com/DualTachyon
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *     Unless required by applicable law or agreed to in writing, software
 *     distributed under the License is distributed on an "AS IS" BASIS,
 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *     See the License for the specific language governing permissions and
 *     limitations under the License.
 */

#include <assert.h>
#include <stdint.h>
#include <string.h>

#include "am_fix.h"
#include "app/action.h"

#ifdef ENABLE_AIRCOPY
    #include "app/aircopy.h"
#endif
#include "app/app.h"
#include "app/chFrScanner.h"
#include "app/dtmf.h"
#ifdef ENABLE_FLASHLIGHT
    #include "app/flashlight.h"
#endif
#ifdef ENABLE_FMRADIO
    #include "app/fm.h"
#endif
#include "app/generic.h"
#include "app/main.h"
#include "app/menu.h"
#include "app/scanner.h"
#ifdef ENABLE_UART
    #include "app/uart.h"
#endif
#include "ARMCM0.h"
#include "audio.h"
#include "board.h"
#include "bsp/dp32g030/gpio.h"
#ifdef ENABLE_FEAT_F4HWN_SLEEP
    #include "bsp/dp32g030/pwmplus.h"
#endif
#include "driver/backlight.h"
#ifdef ENABLE_FMRADIO
    #include "driver/bk1080.h"
#endif
#include "driver/bk4819.h"
#include "driver/gpio.h"
#include "driver/keyboard.h"
#include "driver/st7565.h"
#include "driver/system.h"
#include "dtmf.h"
#include "external/printf/printf.h"
#include "frequencies.h"
#include "functions.h"
#include "helper/battery.h"
#include "misc.h"
#include "radio.h"
#include "settings.h"

#if defined(ENABLE_OVERLAY)
    #include "sram-overlay.h"
#endif
#include "ui/battery.h"
#include "ui/inputbox.h"
#include "ui/main.h"
#include "ui/menu.h"
#include "ui/status.h"
#include "ui/ui.h"

#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
    #include "screenshot.h"
#endif

static bool flagSaveVfo;
static bool flagSaveSettings;
static bool flagSaveChannel;

static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld);


void (*ProcessKeysFunctions[])(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld) = {
    [DISPLAY_MAIN] = &MAIN_ProcessKeys,
    [DISPLAY_MENU] = &MENU_ProcessKeys,
    [DISPLAY_SCANNER] = &SCANNER_ProcessKeys,

#ifdef ENABLE_FMRADIO
    [DISPLAY_FM] = &FM_ProcessKeys,
#endif

#ifdef ENABLE_AIRCOPY
    [DISPLAY_AIRCOPY] = &AIRCOPY_ProcessKeys,
#endif
};

#ifdef ENABLE_REGA
// This is a hack for REGA as I need a special display element only for it with no key
static_assert(ARRAY_SIZE(ProcessKeysFunctions) == DISPLAY_N_ELEM-1);
#else
static_assert(ARRAY_SIZE(ProcessKeysFunctions) == DISPLAY_N_ELEM);
#endif

static void CheckForIncoming(void)
{
    if (!g_SquelchLost)
        return;          // squelch is closed

    // squelch is open

    if (gScanStateDir == SCAN_OFF)
    {   // not RF scanning
        if (gEeprom.DUAL_WATCH == DUAL_WATCH_OFF)
        {   // dual watch is disabled

            #ifdef ENABLE_NOAA
                if (gIsNoaaMode)
                {
                    gNOAA_Countdown_10ms = NOAA_countdown_3_10ms;
                    gScheduleNOAA        = false;
                }
            #endif

            if (gCurrentFunction != FUNCTION_INCOMING)
            {
                FUNCTION_Select(FUNCTION_INCOMING);
                //gUpdateDisplay = true;
            }

            return;
        }

        // dual watch is enabled and we're RX'ing a signal

        if (gRxReceptionMode != RX_MODE_NONE)
        {
            if (gCurrentFunction != FUNCTION_INCOMING)
            {
                FUNCTION_Select(FUNCTION_INCOMING);
                //gUpdateDisplay = true;
            }
            return;
        }

        gDualWatchCountdown_10ms = dual_watch_count_after_rx_10ms;
        gScheduleDualWatch       = false;

        // let the user see DW is not active
        gDualWatchActive = false;
        gUpdateStatus    = true;
    }
    else
    {   // RF scanning
        if (gRxReceptionMode != RX_MODE_NONE)
        {
            if (gCurrentFunction != FUNCTION_INCOMING)
            {
                FUNCTION_Select(FUNCTION_INCOMING);
                //gUpdateDisplay = true;
            }
            return;
        }

        gScanPauseDelayIn_10ms = scan_pause_delay_in_3_10ms;
        gScheduleScanListen    = false;
    }

    gRxReceptionMode = RX_MODE_DETECTED;

    if (gCurrentFunction != FUNCTION_INCOMING)
    {
        FUNCTION_Select(FUNCTION_INCOMING);
        //gUpdateDisplay = true;
    }
}

static void HandleIncoming(void)
{
    if (!g_SquelchLost) {   // squelch is closed
#ifdef ENABLE_DTMF_CALLING
        if (gDTMF_RX_index > 0)
            DTMF_clear_RX();
#endif
        if (gCurrentFunction != FUNCTION_FOREGROUND) {
            FUNCTION_Select(FUNCTION_FOREGROUND);
            gUpdateDisplay = true;
        }
        return;
    }

    bool bFlag = (gScanStateDir == SCAN_OFF && gCurrentCodeType == CODE_TYPE_OFF);

#ifdef ENABLE_NOAA
    if (IS_NOAA_CHANNEL(gRxVfo->CHANNEL_SAVE) && gNOAACountdown_10ms > 0) {
        gNOAACountdown_10ms = 0;
        bFlag               = true;
    }
#endif

    if (g_CTCSS_Lost && gCurrentCodeType == CODE_TYPE_CONTINUOUS_TONE) {
        bFlag       = true;
        gFoundCTCSS = false;
    }

    if (g_CDCSS_Lost && gCDCSSCodeType == CDCSS_POSITIVE_CODE
        && (gCurrentCodeType == CODE_TYPE_DIGITAL || gCurrentCodeType == CODE_TYPE_REVERSE_DIGITAL))
    {
        gFoundCDCSS = false;
    }
    else if (!bFlag)
        return;

#ifdef ENABLE_DTMF_CALLING
    if (gScanStateDir == SCAN_OFF && (gRxVfo->DTMF_DECODING_ENABLE || gSetting_KILLED)) {

        // DTMF DCD is enabled
        DTMF_HandleRequest();
        if (gDTMF_CallState == DTMF_CALL_STATE_NONE) {
            if (gRxReceptionMode != RX_MODE_DETECTED) {
                return;
            }
            gDualWatchCountdown_10ms = dual_watch_count_after_1_10ms;
            gScheduleDualWatch       = false;

            gRxReceptionMode = RX_MODE_LISTENING;

            // let the user see DW is not active
            gDualWatchActive = false;
            gUpdateStatus    = true;

            gUpdateDisplay = true;
            return;
        }
    }
#endif

    APP_StartListening(gMonitor ? FUNCTION_MONITOR : FUNCTION_RECEIVE);
}

static void HandleReceive(void)
{
    #define END_OF_RX_MODE_SKIP 0
    #define END_OF_RX_MODE_END  1
    #define END_OF_RX_MODE_TTE  2

    uint8_t Mode = END_OF_RX_MODE_SKIP;

    if (gFlagTailNoteEliminationComplete)
    {
        Mode = END_OF_RX_MODE_END;
        goto Skip;
    }

    if (gScanStateDir != SCAN_OFF && IS_FREQ_CHANNEL(gNextMrChannel))
    { // we are scanning in the frequency mode
        if (g_SquelchLost)
            return;

        Mode = END_OF_RX_MODE_END;
        goto Skip;
    }

    switch (gCurrentCodeType)
    {
        default:
        case CODE_TYPE_OFF:
            break;

        case CODE_TYPE_CONTINUOUS_TONE:
            if (gFoundCTCSS && gFoundCTCSSCountdown_10ms == 0)
            {
                gFoundCTCSS = false;
                gFoundCDCSS = false;
                Mode        = END_OF_RX_MODE_END;
                goto Skip;
            }
            break;

        case CODE_TYPE_DIGITAL:
        case CODE_TYPE_REVERSE_DIGITAL:
            if (gFoundCDCSS && gFoundCDCSSCountdown_10ms == 0)
            {
                gFoundCTCSS = false;
                gFoundCDCSS = false;
                Mode        = END_OF_RX_MODE_END;
                goto Skip;
            }
            break;
    }

    if (g_SquelchLost)
    {
        #ifdef ENABLE_NOAA
            if (!gEndOfRxDetectedMaybe && !IS_NOAA_CHANNEL(gRxVfo->CHANNEL_SAVE))
        #else
            if (!gEndOfRxDetectedMaybe)
        #endif
        {
            switch (gCurrentCodeType)
            {
                case CODE_TYPE_OFF:
                    if (gEeprom.SQUELCH_LEVEL)
                    {
                        if (g_CxCSS_TAIL_Found)
                        {
                            Mode               = END_OF_RX_MODE_TTE;
                            g_CxCSS_TAIL_Found = false;
                        }
                    }
                    break;

                case CODE_TYPE_CONTINUOUS_TONE:
                    if (g_CTCSS_Lost)
                    {
                        gFoundCTCSS = false;
                    }
                    else
                    if (!gFoundCTCSS)
                    {
                        gFoundCTCSS               = true;
                        gFoundCTCSSCountdown_10ms = 100;   // 1 sec
                    }

                    if (g_CxCSS_TAIL_Found)
                    {
                        Mode               = END_OF_RX_MODE_TTE;
                        g_CxCSS_TAIL_Found = false;
                    }
                    break;

                case CODE_TYPE_DIGITAL:
                case CODE_TYPE_REVERSE_DIGITAL:
                    if (g_CDCSS_Lost && gCDCSSCodeType == CDCSS_POSITIVE_CODE)
                    {
                        gFoundCDCSS = false;
                    }
                    else
                    if (!gFoundCDCSS)
                    {
                        gFoundCDCSS               = true;
                        gFoundCDCSSCountdown_10ms = 100;   // 1 sec
                    }

                    if (g_CxCSS_TAIL_Found)
                    {
                        if (BK4819_GetCTCType() == 1)
                            Mode = END_OF_RX_MODE_TTE;

                        g_CxCSS_TAIL_Found = false;
                    }

                    break;
            }
        }
    }
    else
        Mode = END_OF_RX_MODE_END;

    if (!gEndOfRxDetectedMaybe         &&
         Mode == END_OF_RX_MODE_SKIP   &&
         gNextTimeslice40ms            &&
         gEeprom.TAIL_TONE_ELIMINATION &&
         (gCurrentCodeType == CODE_TYPE_DIGITAL || gCurrentCodeType == CODE_TYPE_REVERSE_DIGITAL) &&
         BK4819_GetCTCType() == 1)
        Mode = END_OF_RX_MODE_TTE;
    else
        gNextTimeslice40ms = false;

Skip:
    switch (Mode)
    {
        case END_OF_RX_MODE_SKIP:
            break;

        case END_OF_RX_MODE_END:
            RADIO_SetupRegisters(true);

            #ifdef ENABLE_NOAA
                if (IS_NOAA_CHANNEL(gRxVfo->CHANNEL_SAVE))
                    gNOAACountdown_10ms = 300;         // 3 sec
            #endif

            gUpdateDisplay = true;

            if (gScanStateDir != SCAN_OFF)
            {

                /*
                switch (gEeprom.SCAN_RESUME_MODE)
                {
                    case SCAN_RESUME_TO:
                        break;

                    case SCAN_RESUME_CO:
                        gScanPauseDelayIn_10ms = scan_pause_delay_in_7_10ms;
                        gScheduleScanListen    = false;
                        break;

                    case SCAN_RESUME_SE:
                        CHFRSCANNER_Stop();
                        break;
                }
                */

                if(gEeprom.SCAN_RESUME_MODE < 81)
                {
                    if(gEeprom.SCAN_RESUME_MODE == 0)
                    {
                        CHFRSCANNER_Stop();
                    }
                    else
                    {
                        gScanPauseDelayIn_10ms = gEeprom.SCAN_RESUME_MODE * (250 / 10); // 250ms
                        gScheduleScanListen    = false;
                    }
                }

                /*
                if(gEeprom.SCAN_RESUME_MODE < 2)
                {
                    gScanPauseDelayIn_10ms = scan_pause_delay_in_6_10ms + (scan_pause_delay_in_6_10ms * 24 * gEeprom.SCAN_RESUME_MODE);
                    gScheduleScanListen    = false;

                }
                else if(gEeprom.SCAN_RESUME_MODE == 2)
                {
                    CHFRSCANNER_Stop();
                }
                */

                /*
                switch (gEeprom.SCAN_RESUME_MODE)
                {
                    case 0:
                        gScanPauseDelayIn_10ms = scan_pause_delay_in_6_10ms;
                        gScheduleScanListen    = false;
                        break;

                    case 1:
                        gScanPauseDelayIn_10ms = scan_pause_delay_in_2_10ms * 5;
                        gScheduleScanListen    = false;
                        break;

                    case 26:
                        CHFRSCANNER_Stop();
                        break;

                    //default:
                    //    gScanPauseDelayIn_10ms = scan_pause_delay_in_5_10ms * (gEeprom.SCAN_RESUME_MODE - 1) * 5;
                    //    break;
                }
                */
            }

            break;

        case END_OF_RX_MODE_TTE:
            if (gEeprom.TAIL_TONE_ELIMINATION) {
                AUDIO_AudioPathOff();

                gTailNoteEliminationCountdown_10ms = 20;
                gFlagTailNoteEliminationComplete   = false;
                gEndOfRxDetectedMaybe = true;
                gEnableSpeaker        = false;
            }
            break;
    }
}

static void HandlePowerSave()
{
    if (!gRxIdleMode) {
        CheckForIncoming();
    }
}

static void (*HandleFunction_fn_table[])(void) = {
    [FUNCTION_FOREGROUND] = &CheckForIncoming,
    [FUNCTION_TRANSMIT] = &FUNCTION_NOP,
    [FUNCTION_MONITOR] = &FUNCTION_NOP,
    [FUNCTION_INCOMING] = &HandleIncoming,
    [FUNCTION_RECEIVE] = &HandleReceive,
    [FUNCTION_POWER_SAVE] = &HandlePowerSave,
    [FUNCTION_BAND_SCOPE] = &FUNCTION_NOP,
};

static_assert(ARRAY_SIZE(HandleFunction_fn_table) == FUNCTION_N_ELEM);

static void HandleFunction(void)
{
    HandleFunction_fn_table[gCurrentFunction]();
}

void APP_StartListening(FUNCTION_Type_t function)
{
    const unsigned int vfo = gEeprom.RX_VFO;

#ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
    gRxTimerCountdown_500ms = 7200;
#endif

#ifdef ENABLE_DTMF_CALLING
    if (gSetting_KILLED)
        return;
#endif

#ifdef ENABLE_FMRADIO
    if (gFmRadioMode)
        BK1080_Init0();
#endif

    // clear the other vfo's rssi level (to hide the antenna symbol)
    gVFO_RSSI_bar_level[!vfo] = 0;

    AUDIO_AudioPathOn();
    gEnableSpeaker = true;

    if (gSetting_backlight_on_tx_rx & BACKLIGHT_ON_TR_RX) {
        BACKLIGHT_TurnOn();
    }

    if (gScanStateDir != SCAN_OFF)
        CHFRSCANNER_Found();

#ifdef ENABLE_NOAA
    if (IS_NOAA_CHANNEL(gRxVfo->CHANNEL_SAVE) && gIsNoaaMode) {
        gRxVfo->CHANNEL_SAVE        = gNoaaChannel + NOAA_CHANNEL_FIRST;
        gRxVfo->pRX->Frequency      = NoaaFrequencyTable[gNoaaChannel];
        gRxVfo->pTX->Frequency      = NoaaFrequencyTable[gNoaaChannel];
        gEeprom.ScreenChannel[vfo] = gRxVfo->CHANNEL_SAVE;

        gNOAA_Countdown_10ms        = 500;   // 5 sec
        gScheduleNOAA               = false;
    }
#endif

    if (gScanStateDir == SCAN_OFF &&
        gEeprom.DUAL_WATCH != DUAL_WATCH_OFF)
    {   // not scanning, dual watch is enabled

        gDualWatchCountdown_10ms = dual_watch_count_after_2_10ms;
        gScheduleDualWatch       = false;

        // when crossband is active only the main VFO should be used for TX
        if(gEeprom.CROSS_BAND_RX_TX == CROSS_BAND_OFF)
            gRxVfoIsActive = true;

        // let the user see DW is not active
        gDualWatchActive = false;
        gUpdateStatus    = true;
    }

    BK4819_WriteRegister(BK4819_REG_48,
        (11u << 12)                |     // ??? .. 0 to 15, doesn't seem to make any difference
        ( 0u << 10)                |     // AF Rx Gain-1
        (gEeprom.VOLUME_GAIN << 4) |     // AF Rx Gain-2
        (gEeprom.DAC_GAIN    << 0));     // AF DAC Gain (after Gain-1 and Gain-2)

#ifdef ENABLE_VOICE
    if (gVoiceWriteIndex == 0)       // AM/FM RX mode will be set when the voice has finished
#endif
        RADIO_SetModulation(gRxVfo->Modulation);  // no need, set it now

    FUNCTION_Select(function);

#ifdef ENABLE_FMRADIO
    if (function == FUNCTION_MONITOR || gFmRadioMode)
#else
    if (function == FUNCTION_MONITOR)
#endif
    {   // squelch is disabled
        if (gScreenToDisplay != DISPLAY_MENU)     // 1of11 .. don't close the menu
            GUI_SelectNextDisplay(DISPLAY_MAIN);
    }
    else
        gUpdateDisplay = true;

    gUpdateStatus = true;
}

uint32_t APP_SetFreqByStepAndLimits(VFO_Info_t *pInfo, int8_t direction, uint32_t lower, uint32_t upper)
{
    uint32_t Frequency = FREQUENCY_RoundToStep(pInfo->freq_config_RX.Frequency + (direction * pInfo->StepFrequency), pInfo->StepFrequency);

#ifdef ENABLE_FEAT_F4HWN
    if (Frequency > upper)
#else
    if (Frequency >= upper)
#endif
        Frequency =  lower;

    else if (Frequency < lower)
        Frequency = FREQUENCY_RoundToStep(upper - pInfo->StepFrequency, pInfo->StepFrequency);

    return Frequency;
}

uint32_t APP_SetFrequencyByStep(VFO_Info_t *pInfo, int8_t direction)
{
    return APP_SetFreqByStepAndLimits(pInfo, direction, frequencyBandTable[pInfo->Band].lower, frequencyBandTable[pInfo->Band].upper);
}

#ifdef ENABLE_NOAA
    static void NOAA_IncreaseChannel(void)
    {
        if (++gNoaaChannel > 9)
            gNoaaChannel = 0;
    }
#endif

static void DualwatchAlternate(void)
{
    #ifdef ENABLE_NOAA
        if (gIsNoaaMode)
        {
            if (!IS_NOAA_CHANNEL(gEeprom.ScreenChannel[0]) || !IS_NOAA_CHANNEL(gEeprom.ScreenChannel[1]))
                gEeprom.RX_VFO = (gEeprom.RX_VFO + 1) & 1;
            else
                gEeprom.RX_VFO = 0;

            gRxVfo = &gEeprom.VfoInfo[gEeprom.RX_VFO];

            if (IS_NOAA_CHANNEL(gEeprom.VfoInfo[0].CHANNEL_SAVE))
                NOAA_IncreaseChannel();
        }
        else
    #endif
    {   // toggle between VFO's
        gEeprom.RX_VFO = !gEeprom.RX_VFO;
        gRxVfo         = &gEeprom.VfoInfo[gEeprom.RX_VFO];

        if (!gDualWatchActive)
        {   // let the user see DW is active
            gDualWatchActive = true;
            gUpdateStatus    = true;
        }
    }

    RADIO_SetupRegisters(false);

    #ifdef ENABLE_NOAA
        gDualWatchCountdown_10ms = gIsNoaaMode ? dual_watch_count_noaa_10ms : dual_watch_count_toggle_10ms;
    #else
        gDualWatchCountdown_10ms = dual_watch_count_toggle_10ms;
    #endif
}

static void CheckRadioInterrupts(void)
{
    if (SCANNER_IsScanning())
        return;

    while (BK4819_ReadRegister(BK4819_REG_0C) & 1u) { // BK chip interrupt request
        // clear interrupts
        BK4819_WriteRegister(BK4819_REG_02, 0);
        // fetch interrupt status bits

        union {
            struct {
                uint16_t __UNUSED : 1;
                uint16_t fskRxSync : 1;
                uint16_t sqlLost : 1;
                uint16_t sqlFound : 1;
                uint16_t voxLost : 1;
                uint16_t voxFound : 1;
                uint16_t ctcssLost : 1;
                uint16_t ctcssFound : 1;
                uint16_t cdcssLost : 1;
                uint16_t cdcssFound : 1;
                uint16_t cssTailFound : 1;
                uint16_t dtmf5ToneFound : 1;
                uint16_t fskFifoAlmostFull : 1;
                uint16_t fskRxFinied : 1;
                uint16_t fskFifoAlmostEmpty : 1;
                uint16_t fskTxFinied : 1;
            };
            uint16_t __raw;
        } interrupts;

        interrupts.__raw = BK4819_ReadRegister(BK4819_REG_02);

        // 0 = no phase shift
        // 1 = 120deg phase shift
        // 2 = 180deg phase shift
        // 3 = 240deg phase shift
//      const uint8_t ctcss_shift = BK4819_GetCTCShift();
//      if (ctcss_shift > 0)
//          g_CTCSS_Lost = true;

        if (interrupts.dtmf5ToneFound) {    
            const char c = DTMF_GetCharacter(BK4819_GetDTMF_5TONE_Code()); // save the RX'ed DTMF character
            if (c != 0xff) {
                if (gCurrentFunction != FUNCTION_TRANSMIT) {
                    if (gSetting_live_DTMF_decoder) {
                        size_t len = strlen(gDTMF_RX_live);
                        if (len >= sizeof(gDTMF_RX_live) - 1) { // make room
                            memmove(&gDTMF_RX_live[0], &gDTMF_RX_live[1], sizeof(gDTMF_RX_live) - 1);
                            len--;
                        }
                        gDTMF_RX_live[len++]  = c;
                        gDTMF_RX_live[len]    = 0;
                        gDTMF_RX_live_timeout = DTMF_RX_live_timeout_500ms;  // time till we delete it
                        gUpdateDisplay        = true;
                    }

#ifdef ENABLE_DTMF_CALLING
                    if (gRxVfo->DTMF_DECODING_ENABLE || gSetting_KILLED) {
                        if (gDTMF_RX_index >= sizeof(gDTMF_RX) - 1) { // make room
                            memmove(&gDTMF_RX[0], &gDTMF_RX[1], sizeof(gDTMF_RX) - 1);
                            gDTMF_RX_index--;
                        }
                        gDTMF_RX[gDTMF_RX_index++] = c;
                        gDTMF_RX[gDTMF_RX_index]   = 0;
                        gDTMF_RX_timeout           = DTMF_RX_timeout_500ms;  // time till we delete it
                        gDTMF_RX_pending           = true;
                        
                        SYSTEM_DelayMs(3);//fix DTMF not reply@Yurisu
                        DTMF_HandleRequest();
                    }
#endif
                }
            }
        }

        if (interrupts.cssTailFound)
            g_CxCSS_TAIL_Found = true;

        if (interrupts.cdcssLost) {
            g_CDCSS_Lost = true;
            gCDCSSCodeType = BK4819_GetCDCSSCodeType();
        }

        if (interrupts.cdcssFound)
            g_CDCSS_Lost = false;

        if (interrupts.ctcssLost)
            g_CTCSS_Lost = true;

        if (interrupts.ctcssFound)
            g_CTCSS_Lost = false;

#ifdef ENABLE_VOX
        if (interrupts.voxLost) {
            g_VOX_Lost         = true;
            gVoxPauseCountdown = 10;

            if (gEeprom.VOX_SWITCH) {
                if (gCurrentFunction == FUNCTION_POWER_SAVE && !gRxIdleMode) {
                    gPowerSave_10ms            = power_save2_10ms;
                    gPowerSaveCountdownExpired = 0;
                }

                if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF && (gScheduleDualWatch || gDualWatchCountdown_10ms < dual_watch_count_after_vox_10ms)) {
                    gDualWatchCountdown_10ms = dual_watch_count_after_vox_10ms;
                    gScheduleDualWatch = false;

                    // let the user see DW is not active
                    gDualWatchActive = false;
                    gUpdateStatus    = true;
                }
            }
        }

        if (interrupts.voxFound) {
            g_VOX_Lost         = false;
            gVoxPauseCountdown = 0;
        }
#endif

        if (interrupts.sqlLost) {
            g_SquelchLost = true;
            BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, true);
            #ifdef ENABLE_FEAT_F4HWN_RX_TX_TIMER
                gRxTimerCountdown_500ms = 7200;
            #endif
        }

        if (interrupts.sqlFound) {
            g_SquelchLost = false;
            BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false);
        }

#ifdef ENABLE_AIRCOPY
        if (interrupts.fskFifoAlmostFull &&
            gScreenToDisplay == DISPLAY_AIRCOPY &&
            gAircopyState == AIRCOPY_TRANSFER &&
            gAirCopyIsSendMode == 0)
        {
            for (unsigned int i = 0; i < 4; i++) {
                g_FSK_Buffer[gFSKWriteIndex++] = BK4819_ReadRegister(BK4819_REG_5F);
            }

            AIRCOPY_StorePacket();
        }
#endif
    }
}

void APP_EndTransmission(void)
{
    // back to RX mode
    RADIO_SendEndOfTransmission();

    gFlagEndTransmission = true;

    if (gMonitor) {
         //turn the monitor back on
        gFlagReconfigureVfos = true;
    }
}

#ifdef ENABLE_VOX
static void HandleVox(void)
{
#ifdef ENABLE_DTMF_CALLING
    if (gSetting_KILLED)
        return;
#endif

    if (gVoxResumeCountdown == 0) {
        if (gVoxPauseCountdown)
            return;
    }
    else {
        g_VOX_Lost         = false;
        gVoxPauseCountdown = 0;
    }

#ifdef ENABLE_FMRADIO
    if (gFmRadioMode)
        return;
#endif

    if (gCurrentFunction == FUNCTION_RECEIVE || gCurrentFunction == FUNCTION_MONITOR)
        return;

    if (gScanStateDir != SCAN_OFF)
        return;

    if (gVOX_NoiseDetected) {
        if (g_VOX_Lost)
            gVoxStopCountdown_10ms = vox_stop_count_down_10ms;
        else if (gVoxStopCountdown_10ms == 0)
            gVOX_NoiseDetected = false;

        if (gCurrentFunction == FUNCTION_TRANSMIT && !gPttIsPressed && !gVOX_NoiseDetected) {
            if (gFlagEndTransmission) {
                //if (gCurrentFunction != FUNCTION_FOREGROUND)
                    FUNCTION_Select(FUNCTION_FOREGROUND);
            }
            else {
                APP_EndTransmission();

                if (gEeprom.REPEATER_TAIL_TONE_ELIMINATION == 0) {
                    //if (gCurrentFunction != FUNCTION_FOREGROUND)
                        FUNCTION_Select(FUNCTION_FOREGROUND);
                }
                else
                    gRTTECountdown_10ms = gEeprom.REPEATER_TAIL_TONE_ELIMINATION * 10;
            }

            gUpdateStatus        = true;
            gUpdateDisplay       = true;
            gFlagEndTransmission = false;
        }
        return;
    }

    if (g_VOX_Lost) {
        gVOX_NoiseDetected = true;

        if (gCurrentFunction == FUNCTION_POWER_SAVE)
            FUNCTION_Select(FUNCTION_FOREGROUND);

        if (gCurrentFunction != FUNCTION_TRANSMIT && !SerialConfigInProgress()) {
#ifdef ENABLE_DTMF_CALLING
            gDTMF_ReplyState = DTMF_REPLY_NONE;
#endif
            RADIO_PrepareTX();
            gUpdateDisplay = true;
        }
    }
}
#endif

void APP_Update(void)
{
#ifdef ENABLE_VOICE
    if (gFlagPlayQueuedVoice) {
            AUDIO_PlayQueuedVoice();
            gFlagPlayQueuedVoice = false;
    }
#endif

#ifdef ENABLE_FEAT_F4HWN
    if (gCurrentFunction == FUNCTION_TRANSMIT && (gTxTimeoutReachedAlert || SerialConfigInProgress()))
    {
        if(gSetting_set_tot >= 2)
        {
            if (gEeprom.BACKLIGHT_TIME == 0) {
                if (gBlinkCounter == 0 || gBlinkCounter == 250)
                {
                    GPIO_FlipBit(&GPIOC->DATA, GPIOC_PIN_FLASHLIGHT);
                }
            }
            else
            {
                if (gBlinkCounter == 0)
                {
                    //BACKLIGHT_TurnOn();
                    BACKLIGHT_SetBrightness(gEeprom.BACKLIGHT_MAX);
                }
                else if(gBlinkCounter == 15000)
                {
                    //BACKLIGHT_TurnOff();
                    BACKLIGHT_SetBrightness(gEeprom.BACKLIGHT_MIN);
                }
            }
        }

        gBlinkCounter++;

        if(
            (gSetting_set_tot == 3 && gEeprom.BACKLIGHT_TIME != 0 && gBlinkCounter > 74000) || 
            (gSetting_set_tot == 3 && gEeprom.BACKLIGHT_TIME == 0 && gBlinkCounter > 79000) || 
            (gSetting_set_tot != 3 && gBlinkCounter > 76000)
            ) // try to calibrate 10 times
        {
            gBlinkCounter = 0;

            if(gSetting_set_tot == 1 || gSetting_set_tot == 3)
            {
                BK4819_DisableScramble();
                BK4819_PlaySingleTone(gTxTimeoutToneAlert, 30, 1, true);
                gTxTimeoutToneAlert += 100;
            }
        }
    }
#endif

    if (gCurrentFunction == FUNCTION_TRANSMIT && (gTxTimeoutReached || SerialConfigInProgress()))
    {   // transmitter timed out or must de-key
        gTxTimeoutReached = false;

#ifdef ENABLE_FEAT_F4HWN
        if(gBacklightCountdown_500ms > 0 || gEeprom.BACKLIGHT_TIME == 61)
        {
            //BACKLIGHT_TurnOn();
            BACKLIGHT_SetBrightness(gEeprom.BACKLIGHT_MAX);
        }

        gTxTimeoutReachedAlert = false;
        gTxTimeoutToneAlert = 800;

        if (gSetting_set_ptt_session) // Improve OnePush if TOT
        {
            if(gPttOnePushCounter == 1)
            {
                gPttOnePushCounter = 3;
            }
            else if(gPttOnePushCounter == 2)
            {
                ProcessKey(KEY_PTT, false, false);
                gPttIsPressed = false;
                gPttOnePushCounter = 0;
                gPttWasReleased = true;
                //if (gKeyReading1 != KEY_INVALID)
                //  gPttWasReleased = true;
            }
            #if defined(ENABLE_FEAT_F4HWN_CTR) || defined(ENABLE_FEAT_F4HWN_INV)
            ST7565_ContrastAndInv();
            #endif
        }
#endif

        APP_EndTransmission();

        AUDIO_PlayBeep(BEEP_880HZ_60MS_DOUBLE_BEEP);

        RADIO_SetVfoState(VFO_STATE_TIMEOUT);

        GUI_DisplayScreen();
    }

    if (gReducedService)
        return;

    if (gCurrentFunction != FUNCTION_TRANSMIT)
        HandleFunction();

#ifdef ENABLE_FMRADIO
//  if (gFmRadioCountdown_500ms > 0)
    if (gFmRadioMode && gFmRadioCountdown_500ms > 0)    // 1of11
        return;
#endif

#ifdef ENABLE_VOICE
    if (!SCANNER_IsScanning() && gScanStateDir != SCAN_OFF && gScheduleScanListen && !gPttIsPressed && gVoiceWriteIndex == 0)
#else
    if (!SCANNER_IsScanning() && gScanStateDir != SCAN_OFF && gScheduleScanListen && !gPttIsPressed)
#endif
    {   // scanning
        CHFRSCANNER_ContinueScanning();
    }

#ifdef ENABLE_NOAA
#ifdef ENABLE_VOICE
        if (gEeprom.DUAL_WATCH == DUAL_WATCH_OFF && gIsNoaaMode && gScheduleNOAA && gVoiceWriteIndex == 0)
#else
        if (gEeprom.DUAL_WATCH == DUAL_WATCH_OFF && gIsNoaaMode && gScheduleNOAA)
#endif
        {
            NOAA_IncreaseChannel();
            RADIO_SetupRegisters(false);

            gNOAA_Countdown_10ms = 7;      // 70ms
            gScheduleNOAA        = false;
        }
#endif

    // toggle between the VFO's if dual watch is enabled
    if (!SCANNER_IsScanning()
        && gEeprom.DUAL_WATCH != DUAL_WATCH_OFF
        && gScheduleDualWatch
        && gScanStateDir == SCAN_OFF
        && !gPttIsPressed
        && gCurrentFunction != FUNCTION_POWER_SAVE
#ifdef ENABLE_VOICE
        && gVoiceWriteIndex == 0
#endif
#ifdef ENABLE_FMRADIO
        && !gFmRadioMode
#endif
#ifdef ENABLE_DTMF_CALLING
        && gDTMF_CallState == DTMF_CALL_STATE_NONE
#endif
    ) {
        DualwatchAlternate();    // toggle between the two VFO's

        if (gRxVfoIsActive && gScreenToDisplay == DISPLAY_MAIN) {
            GUI_SelectNextDisplay(DISPLAY_MAIN);
        }

        gRxVfoIsActive     = false;
        gScanPauseMode     = false;
        gRxReceptionMode   = RX_MODE_NONE;
        gScheduleDualWatch = false;
    }

#ifdef ENABLE_FMRADIO
    if (gScheduleFM && gFM_ScanState != FM_SCAN_OFF && !FUNCTION_IsRx()) {
        // switch to FM radio mode
        FM_Play();
        gScheduleFM = false;
    }
#endif

#ifdef ENABLE_VOX
    if (gEeprom.VOX_SWITCH)
        HandleVox();
#endif

    if (gSchedulePowerSave) {
        if (gPttIsPressed
            || gKeyBeingHeld
            || gEeprom.BATTERY_SAVE == 0
            || gScanStateDir != SCAN_OFF
            || gCssBackgroundScan
            || gScreenToDisplay != DISPLAY_MAIN
#ifdef ENABLE_FMRADIO
            || gFmRadioMode
#endif
#ifdef ENABLE_DTMF_CALLING
            || gDTMF_CallState != DTMF_CALL_STATE_NONE
#endif
#ifdef ENABLE_NOAA
            || (gIsNoaaMode && (IS_NOAA_CHANNEL(gEeprom.ScreenChannel[0]) || IS_NOAA_CHANNEL(gEeprom.ScreenChannel[1])))
#endif
        ) {
            gBatterySaveCountdown_10ms = battery_save_count_10ms;
        } else {
            FUNCTION_Select(FUNCTION_POWER_SAVE);
        }

        gSchedulePowerSave = false;
    }

    if (gPowerSaveCountdownExpired && gCurrentFunction == FUNCTION_POWER_SAVE
#ifdef ENABLE_VOICE
        && gVoiceWriteIndex == 0
#endif
    ) {
        static bool goToSleep;
        // wake up, enable RX then go back to sleep
        if (gRxIdleMode)
        {
            BK4819_Conditional_RX_TurnOn_and_GPIO6_Enable();

#ifdef ENABLE_VOX
            if (gEeprom.VOX_SWITCH)
                BK4819_EnableVox(gEeprom.VOX1_THRESHOLD, gEeprom.VOX0_THRESHOLD);
#endif

            if (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF &&
                gScanStateDir == SCAN_OFF &&
                !gCssBackgroundScan)
            {   // dual watch mode, toggle between the two VFO's
                DualwatchAlternate();
                goToSleep = false;
            }

            FUNCTION_Init();

            gPowerSave_10ms = power_save1_10ms; // come back here in a bit
            gRxIdleMode     = false;            // RX is awake
        }
        else if (gEeprom.DUAL_WATCH == DUAL_WATCH_OFF || gScanStateDir != SCAN_OFF || gCssBackgroundScan || goToSleep)
        {   // dual watch mode off or scanning or rssi update request
            // go back to sleep

#ifdef ENABLE_FEAT_F4HWN_SLEEP
            if(gWakeUp)
            {
                gPowerSave_10ms = gEeprom.BATTERY_SAVE * 200; // deep sleep now indexed on BatSav
            }
            else
            {
                gPowerSave_10ms = gEeprom.BATTERY_SAVE * 10;
            }
#else
            gPowerSave_10ms = gEeprom.BATTERY_SAVE * 10;
#endif
            gRxIdleMode     = true;
            goToSleep = false;

            BK4819_DisableVox();
            BK4819_Sleep();
            BK4819_ToggleGpioOut(BK4819_GPIO0_PIN28_RX_ENABLE, false);

            // Authentic device checked removed

        }
        else {
            // toggle between the two VFO's
            DualwatchAlternate();
            gPowerSave_10ms   = power_save1_10ms;
            goToSleep = true;
        }

        gPowerSaveCountdownExpired = false;
    }
}

// called every 10ms
static void CheckKeys(void)
{
#ifdef ENABLE_DTMF_CALLING
    if(gSetting_KILLED){
        return;
    }
#endif

#ifdef ENABLE_AIRCOPY
    if (gScreenToDisplay == DISPLAY_AIRCOPY && gAircopyState != AIRCOPY_READY){
        return;
    }
#endif

// -------------------- PTT ------------------------
#ifdef ENABLE_FEAT_F4HWN
    if (gSetting_set_ptt_session)
    {
        if (!GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) && !SerialConfigInProgress() && gPttOnePushCounter == 0)
        {   // PTT pressed
            if (++gPttDebounceCounter >= 3)     // 30ms
            {   // start transmitting
                boot_counter_10ms   = 0;
                gPttDebounceCounter = 0;
                gPttIsPressed       = true;
                gPttOnePushCounter = 1;
                ProcessKey(KEY_PTT, true, false);
            }
        }
        else if ((GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) || SerialConfigInProgress()) && gPttOnePushCounter == 1)
        {   
            // PTT released or serial comms config in progress
            if (++gPttDebounceCounter >= 3 || SerialConfigInProgress())     // 30ms
            {   // stop transmitting
                gPttOnePushCounter = 2;
            }
        }
        else if (!GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) && !SerialConfigInProgress() && gPttOnePushCounter == 2)
        {   // PTT pressed again            
            if (++gPttDebounceCounter >= 3 || SerialConfigInProgress())     // 30ms
            {   // stop transmitting
                gPttOnePushCounter = 3;
            }
        }
        else if ((GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) || SerialConfigInProgress()) && gPttOnePushCounter == 3)
        {   // PTT released or serial comms config in progress
            if (++gPttDebounceCounter >= 3 || SerialConfigInProgress())     // 30ms
            {   // stop transmitting
                ProcessKey(KEY_PTT, false, false);
                gPttIsPressed = false;
                if (gKeyReading1 != KEY_INVALID)
                    gPttWasReleased = true;
                gPttOnePushCounter = 0;
                #if defined(ENABLE_FEAT_F4HWN_CTR) || defined(ENABLE_FEAT_F4HWN_INV)
                ST7565_ContrastAndInv();
                #endif
            }
        }
        else
            gPttDebounceCounter = 0;

        //gDebug = gPttOnePushCounter;
    }
    else
    {
        if (gPttIsPressed)
        {
            if (GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) || SerialConfigInProgress())
            {   // PTT released or serial comms config in progress
                if (++gPttDebounceCounter >= 3 || SerialConfigInProgress())     // 30ms
                {   // stop transmitting
                    ProcessKey(KEY_PTT, false, false);
                    gPttIsPressed = false;
                    if (gKeyReading1 != KEY_INVALID)
                        gPttWasReleased = true;
                    #if defined(ENABLE_FEAT_F4HWN_CTR) || defined(ENABLE_FEAT_F4HWN_INV)
                    ST7565_ContrastAndInv();
                    #endif
                }
            }
            else
                gPttDebounceCounter = 0;
        }
        else if (!GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) && !SerialConfigInProgress())
        {   // PTT pressed
            if (++gPttDebounceCounter >= 3)     // 30ms
            {   // start transmitting
                boot_counter_10ms   = 0;
                gPttDebounceCounter = 0;
                gPttIsPressed       = true;
                ProcessKey(KEY_PTT, true, false);
            }
        }
        else
            gPttDebounceCounter = 0;        
    }
#else
    if (gPttIsPressed)
    {
        if (GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) || SerialConfigInProgress())
        {   // PTT released or serial comms config in progress
            if (++gPttDebounceCounter >= 3 || SerialConfigInProgress())     // 30ms
            {   // stop transmitting
                ProcessKey(KEY_PTT, false, false);
                gPttIsPressed = false;
                if (gKeyReading1 != KEY_INVALID)
                    gPttWasReleased = true;
            }
        }
        else
            gPttDebounceCounter = 0;
    }
    else if (!GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) && !SerialConfigInProgress())
    {   // PTT pressed
        if (++gPttDebounceCounter >= 3)     // 30ms
        {   // start transmitting
            boot_counter_10ms   = 0;
            gPttDebounceCounter = 0;
            gPttIsPressed       = true;
            ProcessKey(KEY_PTT, true, false);
        }
    }
    else
        gPttDebounceCounter = 0;
#endif

// --------------------- OTHER KEYS ----------------------------

    // scan the hardware keys
    KEY_Code_t Key = KEYBOARD_Poll();

    if (Key != KEY_INVALID) // any key pressed
        boot_counter_10ms = 0;   // cancel boot screen/beeps if any key pressed

    if (gKeyReading0 != Key) // new key pressed
    {

        if (gKeyReading0 != KEY_INVALID && Key != KEY_INVALID)
            ProcessKey(gKeyReading1, false, gKeyBeingHeld);  // key pressed without releasing previous key

        gKeyReading0     = Key;
        gDebounceCounter = 0;
        return;
    }

    gDebounceCounter++;

    if (gDebounceCounter == key_debounce_10ms) // debounced new key pressed
    {
        if (Key == KEY_INVALID) //all non PTT keys released
        {
            if (gKeyReading1 != KEY_INVALID) // some button was pressed before
            {
                ProcessKey(gKeyReading1, false, gKeyBeingHeld); // process last button released event
                gKeyReading1 = KEY_INVALID;
            }
        }
        else // process new key pressed
        {
            gKeyReading1 = Key;
            ProcessKey(Key, true, false);
        }

        gKeyBeingHeld = false;
        return;
    }

    if (gDebounceCounter < key_repeat_delay_10ms || Key == KEY_INVALID) // the button is not held long enough for repeat yet, or not really pressed
        return;

    if (gDebounceCounter == key_repeat_delay_10ms) //initial key repeat with longer delay
    {
        if (Key != KEY_PTT)
        {
            gKeyBeingHeld = true;
            ProcessKey(Key, true, true); // key held event
        }
    }
    else //subsequent fast key repeats
    {
        if (Key == KEY_UP || Key == KEY_DOWN) // fast key repeats for up/down buttons
        {
            gKeyBeingHeld = true;
            if ((gDebounceCounter % key_repeat_10ms) == 0)
                ProcessKey(Key, true, true); // key held event
        }

        if (gDebounceCounter < 0xFFFF)
            return;

        gDebounceCounter = key_repeat_delay_10ms+1;
    }
}

void APP_TimeSlice10ms(void)
{
    gNextTimeslice = false;
    gFlashLightBlinkCounter++;

#ifdef ENABLE_AM_FIX
    if (gRxVfo->Modulation == MODULATION_AM) {
        AM_fix_10ms(gEeprom.RX_VFO);
    }
#endif

#ifdef ENABLE_UART
    if (UART_IsCommandAvailable()) {
        __disable_irq();
        UART_HandleCommand();
        __enable_irq();
    }
#endif

    if (gReducedService)
        return;

    if (gCurrentFunction != FUNCTION_POWER_SAVE || !gRxIdleMode)
        CheckRadioInterrupts();

    if (gCurrentFunction == FUNCTION_TRANSMIT)
    {   // transmitting
#ifdef ENABLE_AUDIO_BAR
        if (gSetting_mic_bar && (gFlashLightBlinkCounter % (150 / 10)) == 0) // once every 150ms
            UI_DisplayAudioBar();
#endif
    }

    bool gUpdateDisplayCurrent = gUpdateDisplay;
    bool gUpdateStatusCurrent  = gUpdateStatus;

    if (gUpdateDisplayCurrent) {
        gUpdateDisplay = false;
        GUI_DisplayScreen();
    }

    if (gUpdateStatusCurrent) {
        UI_DisplayStatus();
    }

    #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT
    if (gUpdateDisplayCurrent || gUpdateStatusCurrent) {
        getScreenShot(false);
    }
    #endif

    // Skipping authentic device checks

#ifdef ENABLE_FMRADIO
    if (gFmRadioMode && gFmRadioCountdown_500ms > 0)   // 1of11
        return;
#endif

#if !defined(ENABLE_FEAT_F4HWN) || defined(ENABLE_FEAT_F4HWN_RESCUE_OPS)
    #ifdef ENABLE_FLASHLIGHT
        FlashlightTimeSlice();
    #endif
#endif

#ifdef ENABLE_VOX
    if (gVoxResumeCountdown > 0)
        gVoxResumeCountdown--;

    if (gVoxPauseCountdown > 0)
        gVoxPauseCountdown--;
#endif

    if (gCurrentFunction == FUNCTION_TRANSMIT) {
#ifdef ENABLE_ALARM
        if (gAlarmState == ALARM_STATE_TXALARM || gAlarmState == ALARM_STATE_SITE_ALARM) {
            uint16_t Tone;

            gAlarmRunningCounter++;
            gAlarmToneCounter++;

            Tone = 500 + (gAlarmToneCounter * 25);
            if (Tone > 1500) {
                Tone              = 500;
                gAlarmToneCounter = 0;
            }

            BK4819_SetScrambleFrequencyControlWord(Tone);

            if (gEeprom.ALARM_MODE == ALARM_MODE_TONE && gAlarmRunningCounter == 512) {
                gAlarmRunningCounter = 0;

                if (gAlarmState == ALARM_STATE_TXALARM) {
                    gAlarmState = ALARM_STATE_SITE_ALARM;

                    if(gEeprom.TAIL_TONE_ELIMINATION)
                        RADIO_SendCssTail();
                    BK4819_SetupPowerAmplifier(0, 0);
                    BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_PA_ENABLE, false);
                    BK4819_Enable_AfDac_DiscMode_TxDsp();
                    BK4819_ToggleGpioOut(BK4819_GPIO5_PIN1_RED, false);

                    GUI_DisplayScreen();
                }
                else {
                    gAlarmState = ALARM_STATE_TXALARM;

                    GUI_DisplayScreen();

                    BK4819_ToggleGpioOut(BK4819_GPIO5_PIN1_RED, true);
                    RADIO_SetTxParameters();
                    BK4819_TransmitTone(true, 500);
                    SYSTEM_DelayMs(2);
                    AUDIO_AudioPathOn();

                    gEnableSpeaker    = true;
                    gAlarmToneCounter = 0;
                }
            }
        }
#endif
        // repeater tail tone elimination
        if (gRTTECountdown_10ms > 0) {
            if (--gRTTECountdown_10ms == 0) {
                //if (gCurrentFunction != FUNCTION_FOREGROUND)
                    FUNCTION_Select(FUNCTION_FOREGROUND);

                gUpdateStatus  = true;
                gUpdateDisplay = true;
            }
        }
    }

#ifdef ENABLE_FMRADIO
    if (gFmRadioMode && gFM_RestoreCountdown_10ms > 0) {
        if (--gFM_RestoreCountdown_10ms == 0) { 
            FM_Start(); // switch back to FM radio mode
            GUI_SelectNextDisplay(DISPLAY_FM);
        }
    }
#endif


    SCANNER_TimeSlice10ms();

#ifdef ENABLE_AIRCOPY
    if (gScreenToDisplay == DISPLAY_AIRCOPY && gAircopyState == AIRCOPY_TRANSFER && gAirCopyIsSendMode == 1) {
        if (!AIRCOPY_SendMessage()) {
            GUI_DisplayScreen();
        }
    }
#endif

    CheckKeys();
}

void cancelUserInputModes(void)
{
    if (gDTMF_InputMode || gDTMF_InputBox_Index > 0)
    {
        DTMF_clear_input_box();
        gBeepToPlay           = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
        gRequestDisplayScreen = DISPLAY_MAIN;
        gUpdateDisplay        = true;
    }

    if (gWasFKeyPressed || gKeyInputCountdown > 0 || gInputBoxIndex > 0)
    {
        gWasFKeyPressed     = false;
        gInputBoxIndex      = 0;
        gKeyInputCountdown  = 0;
        gBeepToPlay         = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
        gUpdateStatus       = true;
        gUpdateDisplay      = true;
    }
}

// this is called once every 500ms
void APP_TimeSlice500ms(void)
{
    gNextTimeslice_500ms = false;
    bool exit_menu = false;

    // Skipped authentic device check

    if (gKeypadLocked > 0)
        if (--gKeypadLocked == 0)
            gUpdateDisplay = true;

    if (gKeyInputCountdown > 0)
    {
        if (--gKeyInputCountdown == 0)
        {

            if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE) && (gInputBoxIndex == 1 || gInputBoxIndex == 2))
            {
                channelMoveSwitch();

                if (gBeepToPlay == BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL) {
                    AUDIO_PlayBeep(gBeepToPlay);
                }

                SETTINGS_SaveVfoIndices();
            }

            cancelUserInputModes();
        }
    }

    if (gDTMF_RX_live_timeout > 0)
    {
        #ifdef ENABLE_RSSI_BAR
            if (center_line == CENTER_LINE_DTMF_DEC ||
                center_line == CENTER_LINE_NONE)  // wait till the center line is free for us to use before timing out
        #endif
        {
            if (--gDTMF_RX_live_timeout == 0)
            {
                if (gDTMF_RX_live[0] != 0)
                {
                    memset(gDTMF_RX_live, 0, sizeof(gDTMF_RX_live));
                    gUpdateDisplay   = true;
                }
            }
        }
    }

    if (gMenuCountdown > 0)
        if (--gMenuCountdown == 0)
            exit_menu = (gScreenToDisplay == DISPLAY_MENU); // exit menu mode

#ifdef ENABLE_DTMF_CALLING
    if (gDTMF_RX_timeout > 0)
        if (--gDTMF_RX_timeout == 0)
            DTMF_clear_RX();
#endif

    // Skipped authentic device check

#ifdef ENABLE_FMRADIO
    if (gFmRadioCountdown_500ms > 0)
    {
        gFmRadioCountdown_500ms--;
        if (gFmRadioMode)           // 1of11
            return;
    }
#endif

    if (gBacklightCountdown_500ms > 0 && !gAskToSave && !gCssBackgroundScan
        // don't turn off backlight if user is in backlight menu option
        && !(gScreenToDisplay == DISPLAY_MENU && (UI_MENU_GetCurrentMenuId() == MENU_ABR || UI_MENU_GetCurrentMenuId() == MENU_ABR_MAX))
        && --gBacklightCountdown_500ms == 0
        && gEeprom.BACKLIGHT_TIME < 61
    ) {
        BACKLIGHT_TurnOff();
    }

#ifdef ENABLE_FEAT_F4HWN_SLEEP
    if (gSleepModeCountdown_500ms == gSetting_set_off * 120 && gWakeUp) {
        //ST7565_Init();
        ST7565_FixInterfGlitch();
        BK4819_ToggleGpioOut(BK4819_GPIO5_PIN1_RED, false);
        gPowerSave_10ms = gEeprom.BATTERY_SAVE * 10;
        gWakeUp = false;
    }

    #ifdef ENABLE_AIRCOPY
    if(gCurrentFunction != FUNCTION_TRANSMIT && !FUNCTION_IsRx() && gScreenToDisplay != DISPLAY_AIRCOPY)
    #else
    if(gCurrentFunction != FUNCTION_TRANSMIT && !FUNCTION_IsRx())
    #endif
    {
        if (gSleepModeCountdown_500ms > 0 && --gSleepModeCountdown_500ms == 0) {
            gBacklightCountdown_500ms = 0;
            gPowerSave_10ms = 1;
            gWakeUp = true;
            PWM_PLUS0_CH0_COMP = 0;
            ST7565_ShutDown();
        }
        else if(gSleepModeCountdown_500ms != 0 && gSleepModeCountdown_500ms < 21 && gSetting_set_off != 0)
        {
            if(gSleepModeCountdown_500ms % 4 == 0)
            {
                PWM_PLUS0_CH0_COMP = value[gEeprom.BACKLIGHT_MAX] * 4; // Max brightness
            }
            else
            {
                PWM_PLUS0_CH0_COMP = 0;
            }
        }
    }
    else
    {
        gSleepModeCountdown_500ms = gSetting_set_off * 120;
    }

    if (gWakeUp) {
        static uint8_t counter = 0;
        counter = (counter + 1) % 4;
        BK4819_ToggleGpioOut(BK4819_GPIO5_PIN1_RED, (counter == 0));
    }
#endif

    if (gReducedService)
    {
        BOARD_ADC_GetBatteryInfo(&gBatteryCurrentVoltage, &gBatteryCurrent);

        if (gBatteryCurrent > 500 || gBatteryCalibration[3] < gBatteryCurrentVoltage)
        {
            #ifdef ENABLE_OVERLAY
                overlay_FLASH_RebootToBootloader();
            #else
                NVIC_SystemReset();
            #endif
        }

        return;
    }

    gBatteryCheckCounter++;

    // Skipped authentic device check

    if (gCurrentFunction != FUNCTION_TRANSMIT)
    {

        if ((gBatteryCheckCounter & 1) == 0)
        {
            BOARD_ADC_GetBatteryInfo(&gBatteryVoltages[gBatteryVoltageIndex++], &gBatteryCurrent);
            if (gBatteryVoltageIndex > 3)
                gBatteryVoltageIndex = 0;
            BATTERY_GetReadings(true);
        }
    }

    // regular display updates (once every 2 sec) - if need be
    if ((gBatteryCheckCounter & 3) == 0)
    {
        if (gChargingWithTypeC || gSetting_battery_text > 0)
            gUpdateStatus = true;
        #ifdef ENABLE_SHOW_CHARGE_LEVEL
            if (gChargingWithTypeC)
                gUpdateDisplay = true;
        #endif
    }

    if (!gCssBackgroundScan && gScanStateDir == SCAN_OFF && !SCANNER_IsScanning()
#ifdef ENABLE_FMRADIO
        && (gFM_ScanState == FM_SCAN_OFF || gAskToSave)
#endif
#ifdef ENABLE_AIRCOPY
        && gScreenToDisplay != DISPLAY_AIRCOPY
#endif
    ) {
        if (gEeprom.AUTO_KEYPAD_LOCK && gKeyLockCountdown > 0 && !gDTMF_InputMode
            && gScreenToDisplay != DISPLAY_MENU && --gKeyLockCountdown == 0)
        {
            gEeprom.KEY_LOCK = true;     // lock the keyboard
            gUpdateStatus = true;            // lock symbol needs showing
        }

        if (exit_menu) {
            gMenuCountdown = 0;

            if (gEeprom.BACKLIGHT_TIME == 0) {
                BACKLIGHT_TurnOff();
            }

            if (gInputBoxIndex > 0 || gDTMF_InputMode) {
                AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL);
            }
/*
            if (SCANNER_IsScanning()) {
                BK4819_StopScan();

                RADIO_ConfigureChannel(0, VFO_CONFIGURE_RELOAD);
                RADIO_ConfigureChannel(1, VFO_CONFIGURE_RELOAD);

                RADIO_SetupRegisters(true);
            }
*/
            DTMF_clear_input_box();

            gWasFKeyPressed  = false;
            gInputBoxIndex   = 0;

            gAskToSave       = false;
            gAskToDelete     = false;

            gUpdateStatus    = true;
            gUpdateDisplay   = true;

            GUI_DisplayType_t disp = DISPLAY_INVALID;

#ifdef ENABLE_FMRADIO
            if (gFmRadioMode && ! FUNCTION_IsRx()) {
                disp = DISPLAY_FM;
            }
#endif

            if (disp == DISPLAY_INVALID
#ifdef ENABLE_NO_CODE_SCAN_TIMEOUT
                && !SCANNER_IsScanning()
#endif
            ) {
                disp = DISPLAY_MAIN;
            }

            if (disp != DISPLAY_INVALID) {
                GUI_SelectNextDisplay(disp);
            }
        }
    }

    if (!gPttIsPressed && gVFOStateResumeCountdown_500ms > 0 && --gVFOStateResumeCountdown_500ms == 0) {
            RADIO_SetVfoState(VFO_STATE_NORMAL);
#ifdef ENABLE_FMRADIO
        if (gFmRadioMode && !FUNCTION_IsRx()) {
            // switch back to FM radio mode
            FM_Start();
            GUI_SelectNextDisplay(DISPLAY_FM);
        }
#endif
    }

    BATTERY_TimeSlice500ms();
    SCANNER_TimeSlice500ms();
    UI_MAIN_TimeSlice500ms();

#ifdef ENABLE_DTMF_CALLING
    if (gCurrentFunction != FUNCTION_TRANSMIT) {
        if (gDTMF_DecodeRingCountdown_500ms > 0) {
            // make "ring-ring" sound
            gDTMF_DecodeRingCountdown_500ms--;
            AUDIO_PlayBeep(BEEP_880HZ_200MS);
        }
    } else {
        gDTMF_DecodeRingCountdown_500ms = 0;
    }

    if (gDTMF_CallState  != DTMF_CALL_STATE_NONE && gCurrentFunction != FUNCTION_TRANSMIT
        && gCurrentFunction != FUNCTION_RECEIVE && gDTMF_auto_reset_time_500ms > 0
        && --gDTMF_auto_reset_time_500ms == 0)
    {
        gUpdateDisplay  = true;
        if (gDTMF_CallState == DTMF_CALL_STATE_RECEIVED && gEeprom.DTMF_auto_reset_time >= DTMF_HOLD_MAX) {
            gDTMF_CallState = DTMF_CALL_STATE_RECEIVED_STAY;     // keep message on-screen till a key is pressed
        } else {
            gDTMF_CallState = DTMF_CALL_STATE_NONE;
        }
    }

    if (gDTMF_IsTx && gDTMF_TxStopCountdown_500ms > 0 && --gDTMF_TxStopCountdown_500ms == 0) {
        gDTMF_IsTx     = false;
        gUpdateDisplay = true;
    }
#endif
}

#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
static void ALARM_Off(void)
{
    AUDIO_AudioPathOff();
    gEnableSpeaker = false;

    if (gAlarmState == ALARM_STATE_TXALARM || gAlarmState == ALARM_STATE_TX1750) {
        RADIO_SendEndOfTransmission();
    }

    gAlarmState = ALARM_STATE_OFF;

#ifdef ENABLE_VOX
    gVoxResumeCountdown = 80;
#endif

    SYSTEM_DelayMs(5);

    RADIO_SetupRegisters(true);

    if (gScreenToDisplay != DISPLAY_MENU)     // 1of11 .. don't close the menu
        gRequestDisplayScreen = DISPLAY_MAIN;
}
#endif

static void ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
{
    #ifdef ENABLE_FEAT_F4HWN_SLEEP
    if(gWakeUp)
    {
        if(!bKeyPressed || Key == KEY_PTT)
        {
            BACKLIGHT_TurnOn();

            if(Key != KEY_PTT)
            {
                Key = KEY_INVALID;
            }
        }
        else
        {
            return;
        }
    }
    #endif

    if (Key == KEY_EXIT && !BACKLIGHT_IsOn() && gEeprom.BACKLIGHT_TIME > 0)
    {   // just turn the light on for now so the user can see what's what
        BACKLIGHT_TurnOn();
        gBeepToPlay = BEEP_NONE;
        return;
    }

    if (gCurrentFunction == FUNCTION_POWER_SAVE)
        FUNCTION_Select(FUNCTION_FOREGROUND);

    gBatterySaveCountdown_10ms = battery_save_count_10ms;

    if (gEeprom.AUTO_KEYPAD_LOCK)
        gKeyLockCountdown = gEeprom.AUTO_KEYPAD_LOCK * 30;     // 15 seconds step

    if (!bKeyPressed) { // key released
        if (flagSaveVfo) {
            SETTINGS_SaveVfoIndices();
            flagSaveVfo = false;
        }

        if (flagSaveSettings) {
            SETTINGS_SaveSettings();
            flagSaveSettings = false;
        }

#ifdef ENABLE_FMRADIO
        if (gFlagSaveFM) {
            SETTINGS_SaveFM();
            gFlagSaveFM = false;
        }
#endif

        if (flagSaveChannel) {
            SETTINGS_SaveChannel(gTxVfo->CHANNEL_SAVE, gEeprom.TX_VFO, gTxVfo, flagSaveChannel);
            flagSaveChannel = false;

            if (!SCANNER_IsScanning() && gVfoConfigureMode == VFO_CONFIGURE_NONE)
                // gVfoConfigureMode is so as we don't wipe out previously setting this variable elsewhere
                gVfoConfigureMode = VFO_CONFIGURE;
        }
    }
    else { // key pressed or held
        const int m = UI_MENU_GetCurrentMenuId();
        if  (   //not when PTT and the backlight shouldn't turn on on TX
                !(Key == KEY_PTT && !(gSetting_backlight_on_tx_rx & BACKLIGHT_ON_TR_TX))
                // not in the backlight menu
                && !(gScreenToDisplay == DISPLAY_MENU && ( m == MENU_ABR || m == MENU_ABR_MAX || m == MENU_ABR_MIN))
            )
        {
            BACKLIGHT_TurnOn();
        }

        if (Key == KEY_EXIT && bKeyHeld) { // exit key held pressed
            // clear the live DTMF decoder
            if (gDTMF_RX_live[0] != 0) {
                memset(gDTMF_RX_live, 0, sizeof(gDTMF_RX_live));
                gDTMF_RX_live_timeout = 0;
                gUpdateDisplay        = true;
            }

            // cancel user input
            cancelUserInputModes();

            if (gMonitor)
                ACTION_Monitor(); //turn off the monitor
#ifdef ENABLE_SCAN_RANGES
            gScanRangeStart = 0;
#endif
        }

        if (gScreenToDisplay == DISPLAY_MENU)       // 1of11
            gMenuCountdown = menu_timeout_500ms;

#ifdef ENABLE_DTMF_CALLING
        if (gDTMF_DecodeRingCountdown_500ms > 0) { // cancel the ringing
            gDTMF_DecodeRingCountdown_500ms = 0;

            AUDIO_PlayBeep(BEEP_1KHZ_60MS_OPTIONAL);

            if (Key != KEY_PTT) {
                gPttWasReleased = true;
                return;
            }
        }
#endif
    }

    bool lowBatPopup = gLowBattery && !gLowBatteryConfirmed &&  gScreenToDisplay == DISPLAY_MAIN;

#ifdef ENABLE_FEAT_F4HWN // Disable PTT if KEY_LOCK
    bool lck_condition = false;

    if(gSetting_set_lck)
        lck_condition = (gEeprom.KEY_LOCK || lowBatPopup) && gCurrentFunction != FUNCTION_TRANSMIT;
    else
        lck_condition = (gEeprom.KEY_LOCK || lowBatPopup) && gCurrentFunction != FUNCTION_TRANSMIT && Key != KEY_PTT;

    if (lck_condition)
#else
    if ((gEeprom.KEY_LOCK || lowBatPopup) && gCurrentFunction != FUNCTION_TRANSMIT && Key != KEY_PTT)
#endif
    {   // keyboard is locked or low battery popup

        // close low battery popup
        if(Key == KEY_EXIT && bKeyPressed && lowBatPopup) {
            gLowBatteryConfirmed = true;
            gUpdateDisplay = true;
            AUDIO_PlayBeep(BEEP_1KHZ_60MS_OPTIONAL);
            return;
        }

        if (Key == KEY_F) { // function/key-lock key
            if (!bKeyPressed)
                return;

            if (!bKeyHeld) { // keypad is locked, tell the user
                AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL);
                gKeypadLocked  = 4;      // 2 seconds
                gUpdateDisplay = true;
                return;
            }
        }
        // KEY_MENU has a special treatment here, because we want to pass hold event to ACTION_Handle
        // but we don't want it to complain when initial press happens
        // we want to react on realese instead
        else if (Key != KEY_SIDE1 && Key != KEY_SIDE2 &&        // pass side buttons
                 !(Key == KEY_MENU && bKeyHeld)) // pass KEY_MENU held
        {
            if ((!bKeyPressed || bKeyHeld || (Key == KEY_MENU && bKeyPressed)) && // prevent released or held, prevent KEY_MENU pressed
                !(Key == KEY_MENU && !bKeyPressed))  // pass KEY_MENU released
                return;

            // keypad is locked, tell the user
            AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL);
            gKeypadLocked  = 4;          // 2 seconds
            gUpdateDisplay = true;
            return;
        }
    }

    if (Key <= KEY_9 || Key == KEY_F) {
        //if (gScanStateDir != SCAN_OFF || gCssBackgroundScan) { // FREQ/CTCSS/DCS scanning
        if (gCssBackgroundScan) { // FREQ/CTCSS/DCS scanning
            if (bKeyPressed && !bKeyHeld)
                AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL);
            return;
        }
    }

    bool bFlag = false;
    if (Key == KEY_PTT) {
        if (gPttWasPressed) {
            bFlag = bKeyHeld;
            if (!bKeyPressed) {
                bFlag          = true;
                gPttWasPressed = false;
            }
        }
    }
    else if (gPttWasReleased) {
        if (bKeyHeld)
            bFlag = true;
        if (!bKeyPressed) {
            bFlag           = true;
            gPttWasReleased = false;
        }
    }

#ifdef ENABLE_FEAT_F4HWN // For F + SIDE1 or F + SIDE2
    if (gWasFKeyPressed && (Key == KEY_PTT || Key == KEY_EXIT)) { 
#else
    if (gWasFKeyPressed && (Key == KEY_PTT || Key == KEY_EXIT || Key == KEY_SIDE1 || Key == KEY_SIDE2)) { 
#endif
        // cancel the F-key
        gWasFKeyPressed = false;
        gUpdateStatus   = true;
    }

    if (bFlag) {
        goto Skip;
    }

    if (gCurrentFunction == FUNCTION_TRANSMIT) {
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
        if (gAlarmState == ALARM_STATE_OFF)
#endif
        {
            char Code;

            if (Key == KEY_PTT) {
                GENERIC_Key_PTT(bKeyPressed);
                goto Skip;
            }

            if (Key == KEY_SIDE2) { // transmit 1750Hz tone
                Code = 0xFE;
            }
            else {
                Code = DTMF_GetCharacter(Key - KEY_0);
                if (Code == 0xFF)
                    goto Skip;
                // transmit DTMF keys
            }

            if (!bKeyPressed || bKeyHeld) {
                if (!bKeyPressed) {
                    AUDIO_AudioPathOff();

                    gEnableSpeaker = false;

                    BK4819_ExitDTMF_TX(false);

#ifndef ENABLE_FEAT_F4HWN
                    if (gCurrentVfo->SCRAMBLING_TYPE == 0 || !gSetting_ScrambleEnable)
                        BK4819_DisableScramble();
                    else
                        BK4819_EnableScramble(gCurrentVfo->SCRAMBLING_TYPE - 1);
#else
                        BK4819_DisableScramble();
#endif
                }
            }
            else {
                if (gEeprom.DTMF_SIDE_TONE) { // user will here the DTMF tones in speaker
                    AUDIO_AudioPathOn();
                    gEnableSpeaker = true;
                }

                BK4819_DisableScramble();

                if (Code == 0xFE)
                    BK4819_TransmitTone(gEeprom.DTMF_SIDE_TONE, 1750);
                else
                    BK4819_PlayDTMFEx(gEeprom.DTMF_SIDE_TONE, Code);
            }
        }
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
        else if ((!bKeyHeld && bKeyPressed) || (gAlarmState == ALARM_STATE_TX1750 && bKeyHeld && !bKeyPressed)) {
            ALARM_Off();

            if (gEeprom.REPEATER_TAIL_TONE_ELIMINATION == 0)
                FUNCTION_Select(FUNCTION_FOREGROUND);
            else
                gRTTECountdown_10ms = gEeprom.REPEATER_TAIL_TONE_ELIMINATION * 10;

            if (Key == KEY_PTT)
                gPttWasPressed  = true;
            else if (!bKeyHeld)
                gPttWasReleased = true;
        }
#endif
    }
#ifdef ENABLE_FEAT_F4HWN // For F + SIDE1 or F + SIDE2
    else if (gWasFKeyPressed && (Key == KEY_SIDE1 || Key == KEY_SIDE2)) {
        ProcessKeysFunctions[gScreenToDisplay](Key, bKeyPressed, bKeyHeld);
    }
    else if (Key != KEY_SIDE1 && Key != KEY_SIDE2 && gScreenToDisplay != DISPLAY_INVALID) {
        ProcessKeysFunctions[gScreenToDisplay](Key, bKeyPressed, bKeyHeld);
    }
#else
    else if (Key != KEY_SIDE1 && Key != KEY_SIDE2 && gScreenToDisplay != DISPLAY_INVALID) {
        ProcessKeysFunctions[gScreenToDisplay](Key, bKeyPressed, bKeyHeld);
    }
#endif
    else if (!SCANNER_IsScanning()
#ifdef ENABLE_AIRCOPY
            && gScreenToDisplay != DISPLAY_AIRCOPY
#endif
    ) {
        ACTION_Handle(Key, bKeyPressed, bKeyHeld);
    }
    else if (!bKeyHeld && bKeyPressed) {
        gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
    }

Skip:
    if (gBeepToPlay != BEEP_NONE) {
        AUDIO_PlayBeep(gBeepToPlay);
        gBeepToPlay = BEEP_NONE;
    }

    if (gFlagAcceptSetting) {
        gMenuCountdown = menu_timeout_500ms;

        MENU_AcceptSetting();

        gFlagRefreshSetting = true;
        gFlagAcceptSetting  = false;
    }

    if (gRequestSaveSettings) {
        if (!bKeyHeld)
            SETTINGS_SaveSettings();
        else
            flagSaveSettings = 1;
        gRequestSaveSettings = false;
        gUpdateStatus        = true;
    }

#ifdef ENABLE_FMRADIO
    if (gRequestSaveFM) {
        gRequestSaveFM = false;
        if (!bKeyHeld)
            SETTINGS_SaveFM();
        else
            gFlagSaveFM = true;
    }
#endif

    if (gRequestSaveVFO) {
        gRequestSaveVFO = false;
        if (!bKeyHeld)
            SETTINGS_SaveVfoIndices();
        else
            flagSaveVfo = true;
    }

    if (gRequestSaveChannel > 0) { // TODO: remove the gRequestSaveChannel, why use global variable for that??
        if ((!bKeyHeld && !bKeyPressed) || UI_MENU_GetCurrentMenuId())
        {
            SETTINGS_SaveChannel(gTxVfo->CHANNEL_SAVE, gEeprom.TX_VFO, gTxVfo, gRequestSaveChannel);

            if (!SCANNER_IsScanning() && gVfoConfigureMode == VFO_CONFIGURE_NONE)
                // gVfoConfigureMode is so as we don't wipe out previously setting this variable elsewhere
                gVfoConfigureMode = VFO_CONFIGURE;
        }
        else { // this is probably so settings are not saved when up/down button is held and save is postponed to btn release
            flagSaveChannel = gRequestSaveChannel;

            if (gRequestDisplayScreen == DISPLAY_INVALID)
                gRequestDisplayScreen = DISPLAY_MAIN;
        }

        gRequestSaveChannel = 0;
    }

    if (gVfoConfigureMode != VFO_CONFIGURE_NONE) {
        if (gFlagResetVfos) {
            RADIO_ConfigureChannel(0, gVfoConfigureMode);
            RADIO_ConfigureChannel(1, gVfoConfigureMode);
        }
        else
            RADIO_ConfigureChannel(gEeprom.TX_VFO, gVfoConfigureMode);

        if (gRequestDisplayScreen == DISPLAY_INVALID)
            gRequestDisplayScreen = DISPLAY_MAIN;

        gFlagReconfigureVfos = true;
        gVfoConfigureMode    = VFO_CONFIGURE_NONE;
        gFlagResetVfos       = false;
    }

    if (gFlagReconfigureVfos) {
        RADIO_SelectVfos();

#ifdef ENABLE_NOAA
        RADIO_ConfigureNOAA();
#endif

        RADIO_SetupRegisters(true);

#ifdef ENABLE_DTMF_CALLING
        gDTMF_auto_reset_time_500ms = 0;
        gDTMF_CallState             = DTMF_CALL_STATE_NONE;
        gDTMF_TxStopCountdown_500ms = 0;
        gDTMF_IsTx                  = false;
#endif

        gVFO_RSSI_bar_level[0]      = 0;
        gVFO_RSSI_bar_level[1]      = 0;

        gFlagReconfigureVfos        = false;

        if (gMonitor)
            ACTION_Monitor();   // 1of11
    }

    if (gFlagRefreshSetting) {
        gFlagRefreshSetting = false;
        gMenuCountdown      = menu_timeout_500ms;

        MENU_ShowCurrentSetting();
    }

    if (gFlagPrepareTX) {
        RADIO_PrepareTX();
        gFlagPrepareTX = false;
    }

#ifdef ENABLE_VOICE
    if (gAnotherVoiceID != VOICE_ID_INVALID) {
        if (gAnotherVoiceID < 76)
            AUDIO_SetVoiceID(0, gAnotherVoiceID);
        AUDIO_PlaySingleVoice(false);
        gAnotherVoiceID = VOICE_ID_INVALID;
    }
#endif

    GUI_SelectNextDisplay(gRequestDisplayScreen);
    gRequestDisplayScreen = DISPLAY_INVALID;

    gUpdateDisplay = true;
}
